/*
Copyright (c) 2013 Mihail Volkov

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef UNIT_H
#define UNIT_H

#include "unitview.h"
#include <deque>
#include <QMdiSubWindow>
#include <QFile>
#include <QVariant>

class Unit : public QObject
{
	friend class HtMdConnection;

	Q_OBJECT

	unsigned mUnitId;
	UnitView *mpView;
	QMdiSubWindow *mpSubWindow;
	QByteArray mCmdFromApp;
	int mCmdFromAppSofar,mCmdFromAppTotal;
	QUrl mCurrentUrl;
	bool mTurnedIn;

	/// @brief Command from application
	void execAppCommand (QByteArray cmd);

	QString windowTitle ();
	void setWindowTitle (QString);
	QString pageUrl ();
	bool isConnected ();
	bool isTurnedIn ();
	QVariantList clientVersion ();
	QVariantList protocolVersion ();

public:
	explicit Unit(unsigned unitId,QObject *parent = 0);
	~Unit ();
	void loadURL (const QUrl &url);
	void onDisconnected ();

private slots:
	void onUserClose ();
	void bindToPageJS ();
	void urlChanged (QUrl);
	void titleChanged (const QString &);
	
	// signals and public slots are for JS
signals:
	/// @brief Unit disconnected (user disconnect, app disconnected,
	/// connection lost, etc.). Not emitted when the unit is closed
	/// by app request.
	void disconnected ();
	/// @brief Unit reopened (app requested outUnitOpenWindow at the
	/// same URL as it currently is). Upon reception of this signal,
	/// the script is expected to report turnIn and to handle the
	/// situation in whatever way.
	void reopened ();
	/// @brief Received command from the app
	void appCommandReceived (QString appCommand);
	
//public slots:
public:
	/// @brief Report to the unit in the app that the page is ready
	/// to accept commands
	Q_INVOKABLE void turnIn ();
	/// @brief Post command to the unit in the app
	Q_INVOKABLE void postClientCommand (QString clientCommand);
	/// @brief Request to close the unit window. Closing will be
	/// performed asynchronously.
	Q_INVOKABLE void beginClose ();
	/// @brief Unit window url
	Q_PROPERTY(QString pageURL READ pageUrl)
	/// @brief Open a file. Returns a file object (HtMdUnitFile) if
	/// success or null otherwise.
	Q_INVOKABLE QObject *openFile (QString fileName,QString mode);
	/// @brief Return true if connection to the client is established
	Q_PROPERTY(bool isConnected READ isConnected)
	/// @brief Return true if the unit page has turned in
	Q_PROPERTY(bool isTurnedIn READ isTurnedIn)
	/// @brief Return client (program & API) version, array of 3 numbers
	/// [ver_major, ver_minor, ver_revision]
	Q_PROPERTY(QVariantList clientVersion READ clientVersion)
	/// @brief Return protocol version, array of 2 numbers
	/// [ver_major, ver_minor]
	Q_PROPERTY(QVariantList protocolVersion READ protocolVersion)
};


class HtMdUnitFile: public QObject
{
	Q_OBJECT

	QFile mFile;

	double getSize ();
	bool getAtEnd ();
	double getPos ();

public:
	// file name is expected as full htmd: or ass: qualified URL
	HtMdUnitFile (const QString &url,const QString &mode);
	~HtMdUnitFile ();

	bool isOpen ();

	/// @brief Close the file object. It can no longer be used to
	/// read or write the file.
	Q_INVOKABLE void close ();

	/// @brief Return size of the file
	Q_PROPERTY (double size READ getSize)

	/// @brief Return current position of the cursor in the file
	Q_PROPERTY (double pos READ getPos)

	/// @brief True if the file cursor is at EOF
	Q_PROPERTY (bool atEnd READ getAtEnd)

	/// @brief Seek at a certain offset from current position
	/// @param offset Offset to seek. Meaning depends on whence.
	/// @param whence "set"/"SEEK_SET"/anything unrecognized -
	/// offset is 0-based position from start of file,
	/// "cur"/"SEEK_CUR" - offset is relative to current cursor
	/// position (which counts as 0), "end"/"SEEK_END" - offset
	/// is relative to end of file (the position after last byte
	/// counts as 0).
	/// @return true on success, false on failure
	Q_INVOKABLE bool seek (double offset,QString whence);

	/// @brief Read up to 'chars' characters as an UTF-8 string
	/// (actual number in bytes may differ!)
	/// @return The string read
	Q_INVOKABLE QString readText (double chars);

	/// @brief Read up to 'bytes' bytes as an array of numbers
	/// @return Array of bytes actually read
	Q_INVOKABLE QVariantList readBytes (double bytes);

	/// @brief Read a line of UTF-8 chars
	/// @return Line read
	Q_INVOKABLE QString readTextLine ();

	/// @brief Read remaining part of file as a single UTF-8 string
	/// @return The string read
	Q_INVOKABLE QString readAllText ();

	/// @brief Read remaining part of file as an array of UTF-8
	/// string lines
	/// @return The array of strings read
	Q_INVOKABLE QVariantList readAllTextLines ();

	/// @brief Write a string of UTF-8 chars
	/// @return 1 if the string is written entirely, 0 otherwise
	Q_INVOKABLE double writeText (QString);

	/// @brief Write a string of UTF-8 chars and append a line feed
	/// @return 1 if the string is written entirely, 0 otherwise
	Q_INVOKABLE double writeTextLine (QString);

	/// @brief Write a given array of entities. Numbers are written
	/// as bytes, strings are written as writeText-s.
	/// @return Count of array elements successfully written (each
	/// number/byte entry and each string entry count as +1)
	Q_INVOKABLE double write (QVariantList);

	/// @brief Write a given array of entities. Numbers are written
	/// as bytes, strings are written as writeTextLine-s.
	/// @return Count of array elements successfully written (each
	/// number/byte entry and each string entry count as +1)
	Q_INVOKABLE double writeLines (QVariantList);
};

#endif // UNIT_H
